home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / Libraries / SAT 2.3b4 / Demo ƒ / Collision ⁄⁄⁄ demo ƒ / Collision ⁄⁄⁄.p < prev    next >
Text File  |  1995-04-20  |  36KB  |  1,278 lines

  1. {Collision ///}
  2. {}
  3. {This demo demonstrates some alternative ways to use SAT:}
  4. {}
  5. {• The animation is called from the standard event loop (via TransSkel). This slows things down}
  6. {quite a bit (since all other processes are allowed to run), but makes the application background-}
  7. {friendly.}
  8. {• It runs in an ordinary, moveable window. We can, with some effort, do this while still}
  9. {using the fast mode (in which case we would have to restrict window dragging so it stays}
  10. {within the main screen, modify certain global (gSAT.ox ad gSAT.oy), and also limit the}
  11. {horizontal alignment of the window like HyperCard does), but in this demo we just use the}
  12. {slow (safe) mode. }
  13. {• We create sprites from QuickDraw calls instead of cicns.}
  14. {• We use the mask regions of the sprites for better collision detection.}
  15. {• We use a pattern as backkground instead of PICTs.}
  16. {• All the code is in one unit. This might make it less structured, less encapsulated, but I wanted}
  17. {to show you that you don't have to do things exactly the way I do in the other demos.}
  18. {• Using a modified sprite record.}
  19. {• Fixed-point positions}
  20. {}
  21. {However, some variations remain that aren't demonstrated even here:}
  22. {• Calculating the positions of the sprites with the system clock (TickCount or Time Manager) instead of}
  23. {moving them each frame. That approach has some advantages (i.e. constant speed on objects), can easily}
  24. {be used with SAT, but is not as simple.}
  25.  
  26.  
  27. program CollisionIII;
  28.  
  29.     uses
  30. {$ifc UNDEFINED THINK_PASCAL}
  31.         Types, QuickDraw, Events, Windows, Dialogs, Fonts, DiskInit, TextEdit, Traps, Memory, SegLoad,{}
  32.         Scrap, ToolUtils, OSUtils, Menus, Resources, StandardFile, GestaltEqu, Files, Errors, 
  33. {$elsec}
  34.         InterfacesUI,  {To give Think Pascal some UPI}
  35. {$endc}
  36.         TransSkel, customized_SAT;
  37.  
  38.     const
  39.         newgameItem = 1;
  40.         clearHighItem = 4;
  41.  
  42.         aboutAlrt = 128;
  43.         fileMenuRes = 128;
  44.         shapeMenuRes = 129;
  45.         theWindRes = 128;
  46.  
  47.         kGameTime = 3600; {60 sekunder}
  48.         kExtraTime = 180; {3 sekunder}
  49.         kLevelBonus = 25;
  50.  
  51.     type
  52.         SettingsRec = record
  53.                 high: Longint;
  54.                 player: string[5];
  55.             end;
  56.         SettingsPtr = ^SettingsRec;
  57.         SettingsHnd = ^SettingsPtr;
  58.     var
  59.         settings: SettingsHnd;
  60.         fileMenu, shapeMenu: MenuHandle;
  61.         gameRunning: Boolean;
  62.         gameStartTime, lastSetStartTime: Longint;
  63.         setCount: integer;
  64.         gMode: integer;
  65.         scoreFace, highFace, lastface: FacePtr;
  66.         myFace, welcomeFace: FacePtr;
  67.         score: Longint;
  68.         bgPat: SATPatHandle;
  69.  
  70.         scaledFace: array[0..31] of FacePtr;
  71.  
  72.     procedure Barf;
  73.     begin
  74.         SATReportStr('Something went wrong. Sorry.');
  75.         halt;
  76.     end;
  77.  
  78. {Ljud:}
  79.  
  80. {Konstruera en snd-resurs artificiellt}
  81. {Rutinen bygger en handle med reserverad plats för ljudet, som sedan värdrutinen kan skapa.}
  82.     function CreateSnd (size: longint; var sndH: handle; var dataPek: Ptr): Boolean;
  83.         type
  84.             mySndRec = packed record
  85.                     format: integer;
  86.                     numsynth: integer; {must be 0}
  87. {synth}
  88.                     synthid: integer;{5}
  89.                     synthinit: longint;{0}
  90.  
  91.                     numcom: integer; {must be 1}
  92. {command}
  93.                     command: integer;{ $8051}
  94.                     param1: integer; {0}
  95.                     param2: longint; { $14}
  96. {sound header}
  97.                     dataptr: Ptr;
  98.                     datasize: longint;
  99.                     samplerate: longint; {22kHz = $56ee8ba3}
  100.                     loopstart: Ptr;
  101.                     loopend: Ptr;
  102.                     encoding: Byte;{0}
  103.                     basenote: Byte; { $3c}
  104. {data}
  105.                     ljud: packed array[0..0] of Byte;
  106.                 end;
  107.             msrp = ^mySndRec;
  108.             msrh = ^msrp;
  109.         var
  110.             h: msrh;
  111.     begin
  112.         h := msrh(NewHandle(sizeof(mySndRec) + size));
  113.         if h = nil then
  114.             CreateSnd := false
  115.         else
  116.             begin
  117.                 HLock(Handle(h)); {Fixar detta buggen med att ljuden ändras?}
  118.                 with h^^ do
  119.                     begin
  120.                         format := 1;
  121.                         numsynth := 1;
  122.                         synthid := 5;
  123.                         synthinit := 0;
  124.                         numcom := 1;
  125.                         command := $8051;
  126.                         param1 := 0;
  127.                         param2 := $14;
  128.                         dataptr := @ljud[0];
  129.                         datasize := size;
  130.                         samplerate := $56ee8ba3; {div 2 - fast varför köra 11kHz när man synthar?!}
  131.                         loopstart := dataptr;
  132.                         loopend := dataptr; {?}
  133.                         encoding := 0;
  134.                         basenote := $3c;
  135.                         dataPek := dataptr; {Utdata}
  136.                     end; {with}
  137.                 SndH := handle(h);{utdata}
  138.                 CreateSnd := true;
  139.             end; {if nil else}
  140.     end;{CreateSnd}
  141.  
  142.     var
  143.         pushH, bippH, baeH: Handle;
  144.  
  145. {Fixa några bra subrutiner för ljudsyntning?!}
  146. {- Eko}
  147. {- Lågpass och högpass}
  148. {- Sampla upp eller ner?}
  149. {- Frekvensvariation?}
  150. {- Fade in, fade out (mm envelope)}
  151. {Drömmen är förstås FFT, så man kan göra riktigt vass bandspärr, frekvensskift mm.}
  152. {Apropå: kan man inte göra bra ljudkompression med FFT?}
  153.  
  154. {$PUSH}
  155. {$R-}
  156.  
  157. {Rutinen som skall bygga de syntetiska ljud vi önskar!}
  158.     procedure Synth;
  159.         type
  160.             ArtRec = record
  161.                     arr: packed array[0..10000] of Byte;
  162.                 end;
  163.             ArtPtr = ^ArtRec;
  164.         var
  165.             tmpPtr: ArtPtr;
  166.             i: integer;
  167.         const
  168.             pushSize = 3479;
  169.             bippSize = 2959;
  170.             baeSize = 20000;
  171.     begin
  172.         if not CreateSnd(pushSize + 1, pushH, Ptr(tmpptr)) then
  173.             CheckNoMem(nil); {EmergencyExit}
  174.         for i := 0 to pushSize do
  175.             tmpptr^.arr[i] := band(char(random), 127) * (pushSize - i) div pushSize + 128;
  176.         for i := 0 to pushSize - 3 do
  177.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  178.         for i := 0 to 64 do
  179.             begin
  180. {tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;}
  181.                 tmpptr^.arr[pushSize - i] := tmpptr^.arr[pushSize - i] * i div 64;
  182.             end;
  183.  
  184.         if not CreateSnd(bippSize + 1, bippH, Ptr(tmpptr)) then
  185.             CheckNoMem(nil); {EmergencyExit}
  186.         for i := 0 to bippSize do
  187.             tmpptr^.arr[i] := i mod (i div 171 + 1) mod 127 + 128; {mjiioo}
  188. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  189. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  190. {tmpptr^.arr[i] := band(i, 63) + 128;}
  191.         for i := 0 to 64 do
  192.             begin
  193.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  194.                 tmpptr^.arr[bippSize - i] := tmpptr^.arr[bippSize - i] * i div 64;
  195.             end;
  196.  
  197.         if not CreateSnd(baeSize + 1, baeH, Ptr(tmpptr)) then
  198.             CheckNoMem(nil); {EmergencyExit}
  199.         for i := 0 to baeSize do
  200.             tmpptr^.arr[i] := (i div 5) mod (i div 1571 + 1) mod 127 + 128; {mjiioo}
  201. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  202. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  203. {tmpptr^.arr[i] := band(i, 63) + 128;}
  204.         for i := 0 to baeSize - 3 do
  205.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  206.         for i := 0 to 64 do
  207.             begin
  208.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  209.                 tmpptr^.arr[baeSize - i] := tmpptr^.arr[baeSize - i] * i div 64;
  210.             end;
  211.     end;
  212. {$POP}
  213.  
  214.     procedure DoAbout;
  215.     begin
  216.         if 1 = Alert(aboutAlrt, nil) then
  217.             ;
  218.     end;
  219.  
  220. {Two handly routines from my dialog utilities unit.}
  221.     procedure SetTextDItem (theDialog: DialogPtr; itemNo: integer; theString: Str255);
  222.         var
  223.             kind: integer;
  224.             item: ControlHandle;
  225.             box: Rect;
  226.     begin
  227.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  228. {Check kind}
  229.         kind := BitAnd(kind, 127);
  230.         case kind of
  231.             8, 16: {statText, editText}
  232.                 SetDialogItemText(handle(item), theString);
  233.             0, 1, 2, 4, 5, 6: {button, checkbox, radio - men vad är 4?}
  234.                 SetControlTitle(item, theString);
  235.             otherwise {Övriga har ingen text man kan sätta}
  236.                 SysBeep(1);
  237.         end;{case}
  238.     end;
  239.     function GetTextDItem (theDialog: DialogPtr; itemNo: integer): Str255;
  240.         var
  241.             kind: integer;
  242.             item: ControlHandle;
  243.             box: Rect;
  244.             tmpStr: Str255;
  245.     begin
  246.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  247. {Check kind}
  248.         kind := BitAnd(kind, 127);
  249.         tmpStr := '';
  250.         case kind of
  251.             8, 16: {statText, editText}
  252.                 GetDialogItemText(handle(item), tmpStr);
  253.             0, 1, 2, 4, 5, 6: {button, checkbox, radio…?}
  254.                 GetControlTitle(item, tmpStr);
  255.             otherwise {Övriga har ingen text man kan sätta}
  256.                 SysBeep(1);
  257.         end;{case}
  258.         GetTextDItem := tmpStr;
  259.     end;
  260.     function MyNumToString (l: longint): Str255;
  261.         var
  262.             tmpStr: Str255;
  263.     begin
  264.         NumToString(l, tmpStr);
  265.         MyNumToString := tmpStr;
  266.     end;
  267.  
  268. {Make the new high score dialog}
  269.     procedure AskHigh;
  270.         const
  271.             highDlogID = 129;
  272.         var
  273.             dialog: DialogPtr;
  274.             oldPort: GrafPtr;
  275.             itemHit: integer;
  276.             str: str255;
  277.     begin
  278.         GetPort(oldPort);
  279.         dialog := GetNewDialog(highDlogID, nil, WindowPtr(-1));
  280.         ShowWindow(dialog);
  281.         SelectWindow(dialog);
  282.         SetPort(dialog);
  283.  
  284.         SetTextDItem(dialog, 3, settings^^.player);
  285.         SelectDialogItemText(dialog, 3, 0, 32767);
  286.         itemHit := -1;
  287.         while (itemHit <> 1) and (itemHit <> 2) do { 1=ok, 2=cancel }
  288.             ModalDialog(nil, itemHit);
  289.         if itemHit = 1 then
  290.             begin
  291.                 str := GetTextDItem(dialog, 3);
  292.                 if length(str) > 5 then
  293.                     str[0] := char(5); {snabbaste sättet att korta den!}
  294.                 settings^^.player := str;
  295.                 settings^^.high := score;
  296.             end;
  297.         DisposeDialog(dialog);
  298.         SetPort(oldPort);
  299.     end;
  300.  
  301. {Reuseable sprite movement routine, called from all sprite handling routines. Some sprites use this}
  302. {as handling routine.}
  303.     procedure SATBounce (me: SpritePtr);
  304.     begin
  305.         me^.position.h := me^.position.h + me^.speed.h;
  306.         me^.position.v := me^.position.v + me^.speed.v;
  307.         if me^.position.h < 0 then
  308.             me^.speed.h := abs(me^.speed.h);
  309.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  310.             me^.speed.h := -abs(me^.speed.h);
  311.         if me^.position.v < 0 then
  312.             me^.speed.v := abs(me^.speed.v);
  313.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  314.             me^.speed.v := -abs(me^.speed.v);
  315.     end;
  316.  
  317. {The same but using fixed-point position, as in HandlePlayer}
  318.     procedure SATFixedBounce (me: SpritePtr);
  319.     begin
  320.         me^.fixedPos.h := me^.fixedPos.h + me^.speed.h;
  321.         me^.fixedPos.v := me^.fixedPos.v + me^.speed.v;
  322.  
  323.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  324.         me^.position.v := BSR(me^.fixedPos.v, 4);
  325.  
  326. {Since BSR isn't aritmetic shift, a negative fixedPos will unfortunately result in}
  327. {a very large positive position. This must be accounted for when checking borders}
  328. {- or we could use div, but that is slower.}
  329.  
  330.         if me^.fixedPos.h < 0 then
  331.             begin
  332.                 me^.speed.h := abs(me^.speed.h);
  333.                 me^.position.h := 0;
  334.             end
  335.         else if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  336.             me^.speed.h := -abs(me^.speed.h);
  337.         if me^.fixedPos.v < 0 then
  338.             begin
  339.                 me^.speed.v := abs(me^.speed.v);
  340.                 me^.position.v := 0;
  341.             end
  342.         else if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  343.             me^.speed.v := -abs(me^.speed.v);
  344.     end;
  345.  
  346.  
  347.     procedure HandleTheSprite (me: SpritePtr);
  348.     begin
  349.         if me^.speed.h = 0 then
  350.             me^.speed.h := SATRand(32) - SATRand(32);
  351.         if me^.speed.v = 0 then
  352.             me^.speed.v := SATRand(32) - SATRand(32);
  353.         if me^.face = nil then
  354.             begin
  355.                 me^.face := myFace;
  356.                 if me^.face <> nil then
  357.                     me^.hotRect := me^.face^.iconMask.bounds;
  358.             end;
  359.         SATFixedBounce(me);
  360.     end;
  361.  
  362.     procedure RedrawScoreFace;
  363.     begin
  364.         SATSetPortFace(scoreFace);
  365.         EraseRect(scoreFace^.iconMask.bounds);
  366.         MoveTo(2, 14);
  367.         ForeColor(blackColor);
  368.         DrawString('Score:');
  369.         SATDrawLong(score);
  370.         ForeColor(whiteColor);
  371.         MoveTo(0, 12);
  372.         DrawString('Score:');
  373.         SATDrawLong(score);
  374.         ForeColor(blackColor);
  375.         SATSetPortScreen;
  376.         SATSetPortMask(scoreFace);
  377.         EraseRect(scoreFace^.iconMask.bounds);
  378.         MoveTo(0, 12);
  379.         DrawString('Score:');
  380.         SATDrawLong(score);
  381.         MoveTo(2, 14);
  382.         DrawString('Score:');
  383.         SATDrawLong(score);
  384.         SATSetPortScreen;
  385.         SATChangedFace(scoreFace);
  386.     end;
  387.  
  388.     procedure RedrawHighFace;
  389.         var
  390.             str: Str255;
  391.     begin
  392.         str := stringof('High score:', MyNumToString(settings^^.high), ' by ', settings^^.player);
  393.  
  394.         SATSetPortFace(highFace);
  395.         EraseRect(highFace^.iconMask.bounds);
  396.         MoveTo(2, 14);
  397.         ForeColor(blackColor);
  398.         DrawString(str);
  399.         ForeColor(whiteColor);
  400.         MoveTo(0, 12);
  401.         DrawString(str);
  402.         ForeColor(blackColor);
  403.         SATSetPortScreen;
  404.         SATSetPortMask(highFace);
  405.         EraseRect(highFace^.iconMask.bounds);
  406.         MoveTo(0, 12);
  407.         DrawString(str);
  408.         MoveTo(2, 14);
  409.         DrawString(str);
  410.         SATSetPortScreen;
  411.         SATChangedFace(highFace);
  412.     end;
  413.  
  414.     procedure RedrawLastFace;
  415.     begin
  416.         SATSetPortFace(lastface);
  417.         EraseRect(lastface^.iconMask.bounds);
  418.         MoveTo(2, 14);
  419.         DrawString('Last score:');
  420.         SATDrawLong(score);
  421.         ForeColor(whiteColor);
  422.         MoveTo(0, 12);
  423.         DrawString('Last score:');
  424.         SATDrawLong(score);
  425.         ForeColor(blackColor);
  426.         SATSetPortScreen;
  427.         SATSetPortMask(lastface);
  428.         EraseRect(lastface^.iconMask.bounds);
  429.         MoveTo(0, 12);
  430.         DrawString('Last score:');
  431.         SATDrawLong(score);
  432.         MoveTo(2, 14);
  433.         DrawString('Last score:');
  434.         SATDrawLong(score);
  435.         SATSetPortScreen;
  436.         SATChangedFace(lastface);
  437.     end;
  438.  
  439.     var
  440.         playerFace: array[0..15] of FacePtr;
  441.         playerSpeed: array[0..15] of Point;
  442.  
  443.  
  444.  
  445.  
  446.  
  447. {Redraw all player faces. This is separated from InitPlayerFaces since it must be called on}
  448. {depth changes.}
  449.     procedure ReDrawPlayerFaces;
  450.         const
  451.             totalAngle = 240;
  452.         var
  453.             i: integer;
  454.             r, r1, r2, ri: Rect;
  455.             reg1, reg2: RgnHandle;
  456.             pol: PolyHandle;
  457.     begin
  458.         SetRect(r, 0, 0, 40, 40); {Total face size}
  459.         SetRect(r1, 0, 0, 38, 38); {Colored part}
  460.         SetRect(ri, 9, 9, 29, 29); {Colored part, inner circle}
  461.         SetRect(r2, 2, 2, 40, 40); {Shadow}
  462.         for i := 0 to 15 do
  463.             begin
  464.                 reg1 := NewRgn;
  465.                 reg2 := NewRgn;
  466.  
  467. {Generate shape}
  468.                 SATSetPortMask(playerFace[i]);
  469.                 PaintArc(r1, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle);
  470.                 EraseArc(ri, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle); {360-graders-skala}
  471. {$IFC GENERATINGPOWERPC }
  472.                 if noErr <> BitMapToRegion(reg1, playerFace[i]^.iconMask) then{}
  473.                     ;
  474. {$ELSEC}
  475.                 if noErr <> BitMapToRegionGlue(reg1, playerFace[i]^.iconMask) then{}
  476.                     ;
  477. {$ENDC}
  478.                 CopyRgn(reg1, reg2);
  479.                 OffsetRgn(reg2, 2, 2);
  480.  
  481. {Draw face}
  482.                 SATSetPortFace(playerFace[i]);
  483.                 EraseRect(playerFace[i]^.iconMask.bounds);
  484.                 ForeColor(blackColor);
  485.                 PaintRgn(reg2); {black "Shadow"}
  486.                 ForeColor(cyanColor);
  487.                 if gSAT.initDepth > 1 then
  488.                     PaintRgn(reg1) {If we run in color, fill it completely with cyan}
  489.                 else
  490. {$IFC UNDEFINED THINK_PASCAL}
  491.                     FillRgn(reg1, qd.ltGray); {If we run in b/w, a gray pattern looks nicer}
  492. {$ELSEC}
  493.                 FillRgn(reg1, ltGray); {If we run in b/w, a gray pattern looks nicer}
  494. {$ENDC}
  495.                 ForeColor(blueColor);
  496.                 FrameRgn(reg1);
  497.                 ForeColor(blackColor);
  498. {Draw mask}
  499.                 SATSetPortMask(playerFace[i]);
  500.                 EraseRect(playerFace[i]^.iconMask.bounds);
  501.                 PaintRgn(reg1);
  502.                 PaintRgn(reg2);
  503.                 SATSetPortScreen;
  504.                 SATChangedFace(playerFace[i]);
  505.  
  506.                 DisposeRgn(reg1);
  507.                 DisposeRgn(reg2);
  508.             end;
  509.     end;
  510.  
  511. {Create all player faces.}
  512.     procedure InitPlayerFaces;
  513.         var
  514.             i: integer;
  515.             r: Rect;
  516.     begin
  517. {We use crude approximations to the sine/cosine functions we really want.}
  518. {A real game might init the table by using sine and cosine for real, but I don't}
  519. {want to make this harder to read than it already is. A real game would also}
  520. {use more than 16 directions, say 32 or even 64.}
  521.  
  522.         SetPt(playerSpeed[6], 0, -6);
  523.         SetPt(playerSpeed[7], 2, -5);
  524.         SetPt(playerSpeed[8], 4, -4);
  525.         SetPt(playerSpeed[9], 5, -2);
  526.         SetPt(playerSpeed[10], 6, 0);
  527.         SetPt(playerSpeed[11], 5, 2);
  528.         SetPt(playerSpeed[12], 4, 4);
  529.         SetPt(playerSpeed[13], 2, 5);
  530.         SetPt(playerSpeed[14], 0, 6);
  531.         SetPt(playerSpeed[15], -2, 5);
  532.         SetPt(playerSpeed[0], -4, 4);
  533.         SetPt(playerSpeed[1], -5, 2);
  534.         SetPt(playerSpeed[2], -6, 0);
  535.         SetPt(playerSpeed[3], -5, -2);
  536.         SetPt(playerSpeed[4], -4, -4);
  537.         SetPt(playerSpeed[5], -2, -5);
  538.  
  539.         SetPt(playerSpeed[6], 0, -32);
  540.         SetPt(playerSpeed[7], 14, -28);
  541.         SetPt(playerSpeed[8], 22, -22);
  542.         SetPt(playerSpeed[9], 28, -14);
  543.         SetPt(playerSpeed[10], 32, 0);
  544.         SetPt(playerSpeed[11], 28, 14);
  545.         SetPt(playerSpeed[12], 22, 22);
  546.         SetPt(playerSpeed[13], 14, 28);
  547.         SetPt(playerSpeed[14], 0, 32);
  548.         SetPt(playerSpeed[15], -14, 28);
  549.         SetPt(playerSpeed[0], -22, 22);
  550.         SetPt(playerSpeed[1], -28, 14);
  551.         SetPt(playerSpeed[2], -32, 0);
  552.         SetPt(playerSpeed[3], -28, -14);
  553.         SetPt(playerSpeed[4], -22, -22);
  554.         SetPt(playerSpeed[5], -14, -28);
  555.  
  556.         SetRect(r, 0, 0, 40, 40); {Total face size}
  557.         for i := 0 to 15 do
  558.             begin
  559.                 playerFace[i] := SATNewFace(r);
  560.                 SATChangedFace(playerFace[i]);
  561.             end;
  562.         RedrawPlayerFaces;
  563.     end;
  564.  
  565.     procedure HandlePlayer (me: SpritePtr);
  566.     begin
  567.         me^.mode := gMode;
  568.         me^.face := playerFace[me^.mode];
  569.  
  570.         me^.fixedPos.h := me^.fixedPos.h + playerSpeed[me^.mode].h;
  571.         me^.fixedPos.v := me^.fixedPos.v + playerSpeed[me^.mode].v;
  572.  
  573.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  574.         me^.position.v := BSR(me^.fixedPos.v, 4);
  575.  
  576.         if me^.fixedPos.h < 0 then
  577.             begin
  578.                 me^.position.h := 0;
  579.                 me^.fixedPos.h := 0;
  580. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  581.             end;
  582.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  583.             begin
  584.                 me^.position.h := gSAT.offSizeH - me^.hotRect.right;
  585.                 me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  586. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  587.             end;
  588.         if me^.fixedPos.v < 0 then
  589.             begin
  590.                 me^.position.v := 0;
  591.                 me^.fixedPos.v := 0;
  592. {gMode := BitAnd(-gMode, 15);}
  593.             end;
  594.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  595.             begin
  596.                 me^.position.v := gSAT.offSizeV - me^.hotRect.bottom;
  597.                 me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  598. {gMode := BitAnd(-gMode, 15);}
  599.             end;
  600.     end;
  601.  
  602. {Get a vector from center to center of two sprites}
  603.     function Vector (s1, s2: SpritePtr): Point;
  604.     begin
  605.         Vector.h := s1^.position.h + s1^.face^.iconMask.bounds.right div 2 - s2^.position.h - s2^.face^.iconMask.bounds.right div 2;
  606.         Vector.v := s1^.position.v + s1^.face^.iconMask.bounds.right div 2 - s2^.position.v - s2^.face^.iconMask.bounds.right div 2;
  607.     end;
  608.  
  609. {Squared distance between centers of two sprites}
  610.     function Dist2 (s1, s2: SpritePtr): Longint;
  611.         var
  612.             v: Point;
  613.     begin
  614.         v := Vector(s1, s2);
  615.         Dist2 := v.h * v.h + v.v * v.v;
  616.     end;
  617.  
  618.     procedure CreatePill;
  619.     forward;
  620.  
  621.  
  622. {***Check for hits based on regions - reuseable procedure!***}
  623.     function RegionHitTest (s1, s2: SpritePtr): Boolean;
  624.         var
  625.             r1, r2: RgnHandle;
  626.     begin
  627. {We know that out hotRects coincide. However, that doesn't mean that we must take it as a}
  628. {collision! Rather, we can do more processing here to decide whether or not it was a collision.}
  629. {In this case, we copy the mask regions of each sprite, offset them to the proper positions,}
  630. {and check if they, too, overlap!}
  631. {}
  632. {Do you think we are doing double work, both dealing with hotRects and the regions? If you do,}
  633. {let me explain some more. The idea is that SAT checks the hotRects for you, which takes away}
  634. {next to all false hits. Checking hotRects is *fast*, so that's what we can afford to do all-to-all}
  635. {(or all-to-near, depending on the chosen search mode). Once a *possible* collision is detected,}
  636. {we can spend some time analyzing it further!}
  637.  
  638. {First of all, let's do some error checking. We could also have done this when loading the faces.}
  639. {Most programs won't have to bother whether or not the regions have been generated}
  640. {successfully, but when using them this way, they must exist or we may get a crash.}
  641.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  642.             begin
  643.                 SATReportStr('Error: No mask region!');
  644.                 exit(RegionHitTest);
  645.             end;
  646.  
  647. {Make copies of the mask regions and offset them to the proper places.}
  648.         r1 := NewRgn;
  649.         r2 := NewRgn;
  650.         CopyRgn(s1^.face^.maskRgn, r1);
  651.         CopyRgn(s2^.face^.maskRgn, r2);
  652.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  653.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  654.  
  655.         SectRgn(r1, r2, r1);                    {Is there any overlap?}
  656.  
  657. {If empty, no collision, otherwise, handle the collision!}
  658.         RegionHitTest := not EmptyRgn(r1);
  659.  
  660.         DisposeRgn(r1);
  661.         DisposeRgn(r2);
  662.     end;
  663.  
  664. {Collision handling for the player sprite}
  665.     procedure HitPlayer (me, him: SpritePtr);
  666.         var
  667.             v: Point;
  668.     begin
  669.         if RegionHitTest(me, him) then {Do the sprites *really* overlap?}
  670.             begin
  671.  
  672.                 if Dist2(me, him) > 60 then
  673.                     begin
  674. {Hit too far out, so let's call it the outside. Bounce away him.}
  675. {We could make more efforts here for a good bounce.}
  676.                         him^.position.h := him^.position.h + me^.speed.h;
  677.                         him^.speed.h := -him^.speed.h + me^.speed.h;
  678.                         him^.position.v := him^.position.v + me^.speed.v;
  679.                         him^.speed.v := -him^.speed.v + me^.speed.v;
  680. {Finally, make sure the other is moving *away* from us!}
  681. {And when we're at it, why not move it just a little, too?}
  682. {Yuck, this is ugly! Yup, careless programming. Hack, hack!}
  683.                         v := Vector(me, him);
  684.                         if v.h > 0 then
  685.                             begin
  686.                                 if him^.speed.h > 0 then
  687.                                     him^.speed.h := -him^.speed.h;
  688.                                 him^.position.h := him^.position.h - 1;
  689.                             end
  690.                         else
  691.                             begin
  692.                                 if v.h < 0 then
  693.                                     if him^.speed.h < 0 then
  694.                                         him^.speed.h := -him^.speed.h;
  695.                                 him^.position.h := him^.position.h + 1;
  696.                             end;
  697.                         if v.v > 0 then
  698.                             begin
  699.                                 if him^.speed.v > 0 then
  700.                                     him^.speed.v := -him^.speed.v;
  701.                                 him^.position.v := him^.position.v - 1;
  702.                             end
  703.                         else
  704.                             begin
  705.                                 if v.v < 0 then
  706.                                     if him^.speed.v < 0 then
  707.                                         him^.speed.v := -him^.speed.v;
  708.                                 him^.position.v := him^.position.v + 1;
  709.                             end;
  710.  
  711.                     end
  712.                 else
  713.                     begin
  714. {This looks like inside! Let's eat him.}
  715.                         score := score + 1;
  716.                         RedrawScoreFace;
  717.                         him^.task := nil;
  718.                         setCount := setCount - 1;
  719.                         if setCount < 2 then
  720.                             CreatePill; {There should always be pills left!}
  721.                         SATSoundPlay(bippH, 1, true);
  722.                     end; {Dist2}
  723.             end; {RegionHitTest}
  724.  
  725.     end;
  726.  
  727. {Create the score face}
  728.     procedure InitScoreFace;
  729.         var
  730.             r: Rect;
  731.     begin
  732.         SetRect(r, 0, 0, 80, 14);{}
  733.         scoreFace := SATNewFace(r);
  734.         SATChangedFace(scoreFace);
  735.         SetRect(r, 0, 0, 200, 16);{}
  736.         highFace := SATNewFace(r);
  737.         SATChangedFace(highFace);
  738.         SetRect(r, 0, 0, 120, 16);{}
  739.         lastFace := SATNewFace(r);
  740.         SATChangedFace(lastFace);
  741.     end;
  742.  
  743.     procedure SetupDummy (me: SpritePtr);
  744.     begin
  745.         me^.task := @SATBounce;
  746.     end;
  747.  
  748.     procedure SetupSmall (me: SpritePtr);
  749.     begin
  750.         me^.face := myFace;
  751.         me^.hotRect := me^.face^.iconMask.bounds;
  752.         me^.task := @HandleTheSprite;
  753.  
  754.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  755.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  756.     end;
  757.  
  758.     procedure SetupPlayer (me: SpritePtr);
  759.     begin
  760.         me^.face := playerFace[0];
  761.         me^.hotRect := me^.face^.iconMask.bounds;
  762.         me^.task := @HandlePlayer;
  763.         me^.hitTask := @HitPlayer;
  764.  
  765.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  766.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  767.     end;
  768.  
  769.  
  770.     procedure CreatePill;
  771.         var
  772.             sp: SpritePtr;
  773.     begin
  774.         sp := SATNewSprite(-1, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupSmall);
  775.         setCount := setCount + 1; {Number of active pills}
  776.     end;
  777.  
  778.  
  779.     procedure NewSet;
  780.         var
  781.             sp: SpritePtr;
  782.             i: integer;
  783.     begin
  784. {Kill all sprites}
  785.         while gSAT.sRoot <> nil do
  786.             SATKillSprite(gSAT.sRoot);
  787.  
  788. {Create the pills}
  789.         for i := 1 to 10 do
  790.             CreatePill;
  791.         if settings^^.high > 7 then
  792.             for i := 8 to settings^^.high do
  793.                 CreatePill;
  794.  
  795.         sp := SATNewSprite(0, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupDummy);
  796.         RedrawScoreFace;
  797.         sp^.face := scoreFace;
  798.         repeat
  799.             sp^.speed.h := SATRand(5) - 2
  800.         until sp^.speed.h <> 0;
  801.         repeat
  802.             sp^.speed.v := SATRand(3) - 1
  803.         until sp^.speed.v <> 0;
  804.         sp^.hotRect := sp^.face^.iconMask.bounds;
  805. {Hoppsan- fattas nåt!}
  806.  
  807.         sp := SATNewSprite(1, (gSAT.offSizeH - 32) div 2, (gSAT.offSizeV - 32) div 2, @SetupPlayer);
  808.         gMode := 0;
  809.  
  810.         SATBackChanged(gSAT.bounds);
  811.         FlushEvents(6, 0); {Glöm klick från förra uppsättningen!}
  812.         if not (TickCount > gameStartTime + kGameTime) then {Om tiden INTE är ute så skall vi ändra!}
  813.             lastSetStartTime := TickCount;
  814.     end;
  815.  
  816. {An example of how you can (with some effort) scale a sprite.}
  817.     procedure ScaleWelcomeFace;
  818.         var
  819.             srcFacePort, destFacePort: GrafPtr;
  820.             i: integer;
  821.             scaleRect: Rect;
  822.     begin
  823. {Get the rectangle of the original}
  824.         scaleRect := welcomeFace^.iconMask.bounds;
  825.  
  826.         for i := 0 to 31 do
  827.             begin
  828. {SetPortFace to the source. This must be done each turn since ChangedFace changes it.}
  829.                 SATSetPortFace(welcomeFace); {Set the FIRST of SAT's two internal face-ports to the original face.}
  830.                 GetPort(srcFacePort); {Get the port.}
  831. {Modify the size}
  832.                 scaleRect.bottom := scaleRect.bottom - 2;
  833.                 scaleRect.right := scaleRect.right - 2;
  834. {Create the new face}
  835.                 if scaledFace[i] = nil then
  836.                     scaledFace[i] := SATNewFace(scaleRect);
  837. {Get a port to it}
  838.                 SATSetPortFace2(scaledFace[i]); {Set the SECOND of SAT's two internal face ports to the new face.}
  839.                 GetPort(destFacePort); {Get the port.}
  840. {Copy the image}
  841.                 CopyBits(srcFacePort^.portBits, destFacePort^.portBits, welcomeFace^.iconMask.bounds, scaleRect, srcCopy + ditherCopy, nil);
  842.                 CopyBits(welcomeFace^.iconMask, scaledFace[i]^.iconMask, welcomeFace^.iconMask.bounds, scaleRect, srcCopy, nil);
  843.                 SATChangedFace(scaledFace[i]); {Done changing it. Tell SAT that it may do whatever it needs.}
  844.             end; {for}
  845.     end; {ScaleWelcomeFace}
  846.  
  847.     procedure WindUpdate (whatever: Boolean);
  848.         var
  849.             savePort: GrafPtr;
  850.             saveDev: GDHandle;
  851.     begin
  852.         if SATDepthChangeTest then
  853. {IMPORTANT! We must redraw all internally generated faces on depth changes!}
  854.             begin
  855.                 ReDrawPlayerFaces;
  856.                 RedrawScoreFace;
  857.                 RedrawHighFace;
  858.                 RedrawLastFace;
  859.                 ScaleWelcomeFace;
  860.  
  861. {We also have to redraw the background, since it's not a PICT (in which case that is automatic)}
  862.                 GetPort(savePort);
  863.                 if gSAT.colorFlag then
  864.                     saveDev := GetGDevice;
  865.                 SATSetPortBackScreen;
  866.                 SATPenPat(bgPat);
  867.                 PaintRect(gSAT.backScreen.port^.portRect);
  868.                 PenNormal;
  869.                 CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  870.                 SetPort(savePort);
  871.                 if gSAT.colorFlag then
  872.                     SetGDevice(saveDev);
  873.  
  874.             end;
  875.         SATRedraw;
  876.     end;
  877.  
  878.     procedure WindClose;
  879.     begin
  880.         SkelWhoa;
  881.     end;
  882.  
  883.     procedure WindMouse (where: Point; when: Longint; modifiers: integer);
  884.         var
  885.             found, sp: SpritePtr;
  886.             anyLeft: Boolean;
  887.             myRegion: RgnHandle;
  888.     begin
  889. {Not needed for the game, but note that we can check the mask region of a sprite}
  890. {towards a mouse click as well as a colliding sprite! For demonstrating this, mouse}
  891. {clicks are processed, and if a sprite is hit, a SysBeep is made. Try this by clicking}
  892. {in and around the "Hello" sprite!}
  893.  
  894.         myRegion := NewRgn;
  895.         sp := gSAT.sRoot;
  896.         found := nil;
  897.         while sp <> nil do                                        {Search through the sprite list}
  898.             begin
  899.                 if PtInRect(where, sp^.r) then                        {We are in the rect!}
  900.                     if sp^.face <> nil then                            {Does it have a face at all? Remember it's legal not to have one!}
  901.                         if sp^.face^.maskRgn <> nil then                {Does that face have a mask region? It should, but…}
  902.                             begin
  903.                                 CopyRgn(sp^.face^.maskRgn, myRegion);    {Copy the mask region}
  904.                                 OffsetRgn(myRegion, sp^.position.h, sp^.position.v);        {Offset it to the position of the sprite}
  905.                                 if PtInRgn(where, myRegion) then            {Are we in the region?}
  906.                                     found := sp;                                    {Yes!}
  907.                             end;
  908.                 sp := sp^.next;                                        {Next sprite…}
  909.             end;
  910.         if found <> nil then
  911.             SysBeep(1);                                            {We hit something. Tell us so.}
  912.         DisposeRgn(myRegion);
  913.     end;
  914.  
  915.  
  916.     procedure WindKey (theKey: char; theMods: integer);
  917.     begin
  918. {Hard-coded keys; real games have customizable keys.}
  919.         case theKey of
  920.             ',', 'z', '1': 
  921.                 gMode := BitAnd(gMode - 1, 15);
  922.             '.', 'x', '2': 
  923.                 gMode := BitAnd(gMode + 1, 15);
  924.             otherwise
  925.         end; {case}
  926.         ObscureCursor; {Hide the cursor until the mouse is moved.}
  927.     end;
  928.  
  929.     procedure SetupSAT (theWind: WindowPtr);{Calls SATCustomInit and paints the background with a pattern}
  930.         var
  931.             savePort: SATPort;
  932.             r: Rect;
  933.     begin
  934.         SATGetPort(savePort);
  935.  
  936.         SetPort(theWind);
  937.         r := theWind^.portRect;
  938.         OffsetRect(r, -r.left, -r.top);
  939.         SATCustomInit(0, 0, r, theWind, nil, false, false, false, true, false); {Nytt försök!}
  940. {SATCustomInit(0, 0, theWind^.portRect, theWind, nil, false, false, false, true, false); {Nytt försök!}
  941.  
  942. {We use a customized sprite record! Thus, we must SetSpriteSize before creating sprites!}
  943.         SATSetSpriteRecSize(sizeof(Sprite));
  944.  
  945.         if bgPat = nil then
  946.             bgPat := SATGetPat(128);
  947.         if bgPat = nil then
  948.             Barf;
  949.  
  950.         SATSetPortBackScreen;
  951.         SATPenPat(bgPat);
  952.         PaintRect(gSAT.backScreen.port^.portRect);
  953.         PenNormal;
  954.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  955.  
  956.         SATSetPort(savePort);
  957.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.wind.port^.portBits, gSAT.wind.port^.portRect, gSAT.wind.port^.portRect, srcCopy, nil);
  958.  
  959.         if SkelWindow(theWind, @WindMouse, @WindKey, @WindUpdate, nil, @WindClose, nil, nil, false) then
  960.             ;
  961.  
  962.     end;
  963.  
  964.     procedure SetupWindow;
  965.         var
  966.             slaskWind, theWind: WindowPtr;
  967.             tmpWorld: SysEnvRec;
  968.             tmpCol: Boolean;
  969. {r: Rect;}
  970. {peek: WindowPeek;}
  971.     begin
  972.         tmpCol := false;
  973.         if noErr = SysEnvirons(1, tmpWorld) then
  974.             tmpCol := tmpWorld.hasColorQD;
  975.  
  976.         if tmpCol then
  977.             theWind := GetNewCWindow(theWindRes, nil, WindowPtr(-1))
  978.         else
  979.             theWind := GetNewWindow(theWindRes, nil, WindowPtr(-1));
  980.  
  981. {peek := WindowPeek(theWind);}
  982.  
  983.         if theWind = nil then
  984.             Barf;
  985.  
  986. {MoveWindow(theWind, 50, 50, false);}
  987.  
  988. {r := WindowPeek(theWind)^.contRgn^^.rgnBBox;}
  989.  
  990. {slaskWind := theWind;}
  991.  
  992.         SetupSAT(theWind); {Calls SATCustomInit and paints the background with a pattern}
  993.  
  994.         SATSetPortScreen;
  995.         ShowWindow(gSAT.wind.port);
  996.         SelectWindow(gSAT.wind.port);
  997.         SATRedraw;
  998.     end;
  999.  
  1000. {The task the welcome sprite has while zooming.}
  1001.     procedure ZoomWelcome (me: SpritePtr);
  1002.     begin
  1003.         me^.mode := me^.mode + 1;
  1004. {Compensate for the size change to make it centered in one place.}
  1005.         me^.position.h := me^.position.h - 1;
  1006.         me^.position.v := me^.position.v - 1;
  1007.         if me^.mode >= 32 then
  1008.             begin
  1009.                 me^.face := welcomeFace;
  1010.                 me^.task := @SATBounce;
  1011.             end
  1012.         else
  1013.             me^.face := scaledFace[32 - me^.mode];
  1014.     end;
  1015.  
  1016. {Initialize faces.}
  1017.     procedure InitSpriteFaces;
  1018.         var
  1019.             i: integer;
  1020.     begin
  1021.         myFace := SATGetFace(128);
  1022.         if myFace = nil then
  1023.             Barf;
  1024.         welcomeFace := SATGetFace(138);
  1025.         if welcomeFace = nil then
  1026.             Barf;
  1027. {We don't HAVE to bail out when a face fails to load - the program will stll wor, but that face will}
  1028. {not be visible.}
  1029.         ScaleWelcomeFace;
  1030.     end;
  1031.  
  1032.     var
  1033.         lastTime: Longint;
  1034.  
  1035. {DirtyWork is called from TransSkel}
  1036.     procedure DirtyWork;
  1037.         var
  1038.             sp: SpritePtr;
  1039.             ph: PicHandle;
  1040.             r: Rect;
  1041.             savePort: GrafPtr;
  1042.             saveDev: GDHandle;
  1043.     begin
  1044. {We can check TickCount as usual, since we never know how often we get null events.}
  1045.         if lastTime + 1 < TickCount then
  1046.             begin
  1047.                 SATRun(false);
  1048.                 lastTime := tickCount;
  1049.             end;
  1050.  
  1051.         if gameRunning then
  1052.             begin
  1053. {Timebar}
  1054.                 GetPort(savePort);
  1055.                 if gSAT.colorFlag then
  1056.                     saveDev := GetGDevice;
  1057.                 SATSetPortBackScreen;
  1058. {I *should* change only the part that actually changes!}
  1059.                 r := gSAT.wind.port^.portRect;
  1060.                 SATBackChanged(r);
  1061.                 r.right := 5;
  1062.                 r.top := r.bottom * (lastSetStartTime + kGameTime - TickCount) div kGameTime;
  1063.                 ForeColor(redColor); {Quickest way to get a color.}
  1064.                 PaintRect(r);
  1065.                 r.bottom := r.top;
  1066.                 r.top := 0;
  1067.                 SATPenPat(bgPat);
  1068.                 PaintRect(r);
  1069.                 PenNormal;
  1070.  
  1071.                 SetPort(savePort);
  1072.                 if gSAT.colorFlag then
  1073.                     SetGDevice(saveDev);
  1074. {end of Timebar}
  1075.  
  1076.                 if TickCount > lastSetStartTime + kGameTime then
  1077.                     begin
  1078.                         SATSoundPlay(baeH, 5, true);
  1079. {NewSet;}
  1080.  
  1081.                         if TickCount > gameStartTime + kGameTime then
  1082.                             begin
  1083.                                 if score > settings^^.high then
  1084.                                     begin
  1085. {settings^^.high := score;}
  1086.                                         SATSoundEvents;
  1087.                                         AskHigh;
  1088.                                         ChangedResource(Handle(settings));
  1089.                                     end;
  1090.  
  1091. {Kill all sprites}
  1092.                                 while gSAT.sRoot <> nil do
  1093.                                     SATKillSprite(gSAT.sRoot);
  1094.  
  1095.                                 RedrawHighFace;
  1096.                                 RedrawLastFace;
  1097. {Time for breaking some of my conventions! The stuff below should be done in "setup" and "handle"}
  1098. {routines, as recommened in the manual and done in other demos - but if we want to mess up the code,}
  1099. {we are free to do so! The sprites below set up their faces and speeds right here, and share a common}
  1100. {handling routine (SATBounce).}
  1101.  
  1102. {Make the "hello" sprite}
  1103.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1104.                                 sp^.face := welcomeFace;
  1105.                                 repeat
  1106.                                     sp^.speed.h := SATRand(3) - 1
  1107.                                 until sp^.speed.h <> 0;
  1108.                                 repeat
  1109.                                     sp^.speed.v := SATRand(3) - 1
  1110.                                 until sp^.speed.v <> 0;
  1111.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1112.                                 sp^.task := @ZoomWelcome;
  1113. {High score sprite}
  1114.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 4, @SetupDummy);
  1115.                                 sp^.face := highFace;
  1116.                                 repeat
  1117.                                     sp^.speed.h := SATRand(7) - 3
  1118.                                 until sp^.speed.h <> 0;
  1119.                                 repeat
  1120.                                     sp^.speed.v := SATRand(3) - 1
  1121.                                 until sp^.speed.v <> 0;
  1122.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1123. {Last score sprite}
  1124.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 3, @SetupDummy);
  1125.                                 sp^.face := lastFace;
  1126.                                 repeat
  1127.                                     sp^.speed.h := SATRand(7) - 3
  1128.                                 until sp^.speed.h <> 0;
  1129.                                 repeat
  1130.                                     sp^.speed.v := SATRand(3) - 1
  1131.                                 until sp^.speed.v <> 0;
  1132.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1133.  
  1134.                                 SATSetPortScreen;
  1135.                                 SATRedraw; {Just to make sure killed sprites are erased}
  1136.                                 gameRunning := false;
  1137.                             end;
  1138.  
  1139.                     end;
  1140.             end;
  1141.  
  1142.         if not gameRunning then
  1143.             if gSAT.sRoot = nil then
  1144.                 begin
  1145. {Messy code for setting up the "hello" sprite - which is why I recommend the use of setup routines.}
  1146.                     sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1147.                     sp^.face := welcomeFace;
  1148.                     repeat
  1149.                         sp^.speed.h := SATRand(3) - 1
  1150.                     until sp^.speed.h <> 0;
  1151.                     repeat
  1152.                         sp^.speed.v := SATRand(3) - 1
  1153.                     until sp^.speed.v <> 0;
  1154.                     sp^.hotRect := sp^.face^.iconMask.bounds;
  1155.                     sp^.task := @ZoomWelcome;
  1156.                 end;
  1157.     end;
  1158.  
  1159.     procedure InitHigh;
  1160.     begin
  1161.         settings := SettingsHnd(GetResource('Sett', 0));
  1162.         if settings = nil then {Didn't exist - create it!}
  1163.             begin
  1164.                 settings := SettingsHnd(NewHandle(Sizeof(SettingsRec)));
  1165.                 if settings = nil then
  1166.                     begin
  1167.                         SysBeep(1);
  1168.                         halt;
  1169.                     end;
  1170.                 settings^^.high := 0;
  1171.                 AddResource(handle(settings), 'Sett', 0, '');
  1172.             end
  1173.         else {Did exist - check the size!}
  1174.             if GetHandleSize(Handle(settings)) < sizeof(SettingsRec) then
  1175.                 SetHandleSize(Handle(settings), sizeof(SettingsRec));
  1176.     end;
  1177.  
  1178.     procedure DoFileMenu (item: integer);
  1179.     begin
  1180.         case item of
  1181.             newGameItem: 
  1182.                 begin
  1183.                     score := 0;
  1184.                     gameRunning := true;
  1185.                     gameStartTime := TickCount;
  1186.                     lastSetStartTime := TickCount;
  1187.                     setCount := 0;
  1188.                     NewSet;
  1189.  
  1190.                     ObscureCursor; {Hide the cursor until the mouse is moved.}
  1191.                 end;
  1192.             clearHighItem: 
  1193.                 if SATQuestionStr('Set the high score to zero?') then
  1194.                     begin
  1195.                         settings^^.high := 0;
  1196.                         ChangedResource(handle(settings));
  1197.                     end;
  1198.             otherwise
  1199.                 SkelWhoa;
  1200.         end;
  1201.     end;
  1202.  
  1203.     procedure DoShapeMenu (item: integer);
  1204.         const
  1205.             wide = 1;
  1206.             tall = 2;
  1207.         var
  1208.             p: Point;
  1209.     begin
  1210.         p := gSAT.wind.port^.portRect.botRight;
  1211.         case item of
  1212.             wide: 
  1213.                 if gSAT.wind.port^.portRect.bottom > gSAT.wind.port^.portRect.right then
  1214.                     begin
  1215.                         CheckItem(shapeMenu, wide, true);
  1216.                         CheckItem(shapeMenu, tall, false);
  1217.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1218.                         SATKill;
  1219.                         SetupSAT(gSAT.wind.port);
  1220.                         gameRunning := false;
  1221.                     end;
  1222.             tall: 
  1223.                 if gSAT.wind.port^.portRect.bottom < gSAT.wind.port^.portRect.right then
  1224.                     begin
  1225.                         CheckItem(shapeMenu, tall, true);
  1226.                         CheckItem(shapeMenu, wide, false);
  1227.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1228.                         SATKill;
  1229.                         SetupSAT(gSAT.wind.port);
  1230.                         gameRunning := false;
  1231.                     end;
  1232.             otherwise
  1233.                 SysBeep(1);
  1234.         end;{case}
  1235.     end;
  1236.  
  1237.     procedure SetUpMenus;
  1238.     begin
  1239.         SkelApple('About CollisionIII…', @DoAbout);
  1240.         fileMenu := GetMenu(fileMenuRes);
  1241.         if fileMenu = nil then
  1242.             Barf;
  1243.         if SkelMenu(fileMenu, @DoFileMenu, nil, true) then
  1244.             ;
  1245.         shapeMenu := GetMenu(shapeMenuRes);
  1246.         if shapeMenu = nil then
  1247.             Barf;
  1248.         if SkelHMenu(shapeMenu, @DoShapeMenu, nil) then {Install as hierarcical menu}
  1249.             ;
  1250.         CheckItem(shapeMenu, 1, true); {Check "wide"}
  1251.     end;
  1252.  
  1253. begin
  1254.     SkelInit(6, nil);
  1255.     SkelSetSleep(0); {Tell TransSkel that we want attention as often as possible.}
  1256.     SetupMenus;
  1257.     SetupWindow;
  1258.  
  1259.     InitHigh;
  1260.     InitSpriteFaces;
  1261.     InitScoreFace;
  1262.     InitPlayerFaces;
  1263.  
  1264.     SkelBackground(@DirtyWork);
  1265.     lastTime := TickCount;
  1266. {$IFC UNDEFINED THINK_PASCAL}
  1267.     qd.randSeed := TickCount;
  1268. {$ELSEC}
  1269.     randSeed := TickCount;
  1270. {$ENDC}
  1271.  
  1272.     Synth; {Build sounds!}
  1273.     SATSoundShutup;
  1274.  
  1275.     SkelMain;
  1276.     SkelClobber;
  1277.     SATSoundShutup;
  1278. end.